﻿/*
-----------------------------------------------------------------------------
This source file is part of Cell Cloud.

Copyright (c) 2009-2012 Cell Cloud Team - cellcloudproject@gmail.com

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-----------------------------------------------------------------------------
*/

#ifndef CellIniFile_h
#define CellIniFile_h

#include "CellPrerequisites.h"

#ifdef _WIN32
	// VC6 "identifier was truncated to '255' characters in the debug information"
	#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
		#pragma warning(disable: 4786)
	#endif
	// Prevent compile time warnings for deprecation
	#ifndef _CRT_SECURE_NO_DEPRECATE
		#define _CRT_SECURE_NO_DEPRECATE
	#endif
#endif

#include <set>
#include <string>
#include <string.h>
#include <wchar.h>
#include <algorithm>

namespace cell
{
/** \addtogroup Core
 *  @{
 */
/** \addtogroup General
 *  @{
 */

#define INI_TOKEN_A_ANSI "\a"	// I.E. Item1;Item2;Item3 - '\a' used in place of ';'
#define INI_TOKEN_B_ANSI "\b"	// I.E. Item1,Item1b;Item2,Item2b;Item3,Item3b - '\b' used in place of ','
#define INI_EMPTY_ANSI "*"		// Used to indicate empty value in token string. I.E. *;Item2;*;Item3;

/** ANSI 版 IniFile
*/
class _CellExport IniFileA : public GeneralAlloc
{
public:
	static const char* const LF;

public:
	/**
	*/
	IniFileA(void);

	/**
	*/
	virtual ~IniFileA(void);

	/** Used to save the data back to the file.
	*/
	bool save(const std::string& fileName);

	/** Save data to an output stream.
	*/
	void save(std::ostream& output);

	/** Loads the Reads the data in the ini file into the IniFile object.
	*/
	bool load(const std::string& fileName , bool merge = false);

	/** Load data from an input stream
	*/
	void load(std::istream& input , bool merge = false);

public:
	/**
	*/
	class _CellExport IniMergeA : public GeneralAlloc
	{
	public:
		explicit IniMergeA(IniFileA& ini);

		std::istream &operator()(std::istream& input) const;

	private:
		IniFileA& mIni;
	};

public:

#ifdef _WIN32
// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
	struct ci_less_a;
#endif
#endif

	/**
	*/
	class _CellExport IniSectionA : public GeneralAlloc
	{
		friend class IniFileA; // Allow IniFileA to create sections

	#ifdef _WIN32
	// Added for versions earlier than VS2008
	#if defined(_MSC_VER) && (_MSC_VER <= 1400)
		friend struct ci_less_a;
	#endif
	#endif

	public:

		#ifdef _WIN32
		// Added for versions earlier than VS2008
		#if defined(_MSC_VER) && (_MSC_VER <= 1400)
			struct ci_less_a;
		#endif
		#endif

		class _CellExport IniKeyA : public GeneralAlloc
		{
			friend class IniSectionA; // Allow IniSectionA to create keys

		#ifdef _WIN32
			// Added for versions earlier than VS2008
		#if defined(_MSC_VER) && (_MSC_VER <= 1400)
			friend struct ci_less_a;
		#endif
		#endif

		private:
			// IniFileA acts as a class factory for IniSectionA Objects
			IniKeyA(IniSectionA* section , const std::string& keyName);
			IniKeyA(const IniKeyA& other);
			IniKeyA& operator=(const IniKeyA& other);
			virtual ~IniKeyA(void);

		public:
			// Sets the value of the key
			void setValue(const std::string& value);

			// Returns the value of the key
			const std::string& getValue(void) const;

			// Sets the key name, returns true on success, fails if the section
			// name keyName already exists
			bool setKeyName(const std::string& keyName);

			// Returns the name of the Key
			const std::string& getKeyName(void) const;

		private:
			// Pointer to the parent IniSectionA
			IniSectionA* mSection;
			// Name of the Key
			std::string mKeyName;
			// Value associated
			std::string mValue;
		};

		// Typedef of set of IniKeyA pointers
		struct ci_less_a
		{
			ci_less_a(void);
			~ci_less_a(void);

			bool operator() (const IniKeyA* s1, const IniKeyA* s2) const;
		};

		typedef std::set<IniKeyA*, ci_less_a> KeyIndexA;

	#ifdef _WIN32
		// Added for VC6 Support
	#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
		friend class IniKeyA;
	#endif
	#endif

	private:
		// IniSectionA acts as a class factory for IniKeyA Objects
		IniSectionA(IniFileA* iniFile , const std::string& sectionName);
		IniSectionA(const IniSectionA&);
		IniSectionA& operator=(const IniSectionA&);
		virtual ~IniSectionA(void);

	public:
		// Adds a key to the IniSectionA object, returns a IniKeyA pointer to the new or existing object
		IniKeyA* addKey(const std::string& keyName);

		// Removes a single key by pointer
		void removeKey(IniKeyA* key);

		// Removes a single key by string
		void removeKey(const std::string& key);

		// Removes all the keys in the section
		void removeAllKeys(void);

		// Returns a IniKeyA pointer to the key by name, NULL if it was not found
		IniKeyA* getKey(const std::string& keyName ) const;

		// Returns all keys in the section by KeyIndex only to be used for enumeration
		const KeyIndexA& getKeys(void) const;

		// Returns a KeyValue at a certain section
		std::string getKeyValue(const std::string& key) const;

		// Sets a KeyValuePair at a certain section
		void setKeyValue(const std::string& key, const std::string& value);

		// Sets the section name, returns true on success, fails if the section
		// name sSectionName already exists
		bool setSectionName(const std::string& sectionName);

		// Returns the section name
		const std::string& getSectionName(void) const;

	private:

		KeyIndexA::const_iterator _find_key(const std::string& keyName) const;
		KeyIndexA::iterator _find_key(const std::string& keyName);

	private:
		// IniFileA pointer back to the object that instanciated the section
		IniFileA* mIniFile;
		// Name of the section
		std::string mSectionName;
		// List of IniKeyA pointers ( Keys in the section )
		KeyIndexA mKeys;
	};

	// Typedef of a List of IniSectionA pointers
	struct ci_less_a
	{
		ci_less_a(void);
		~ci_less_a(void);

		bool operator() (const IniSectionA* s1, const IniSectionA* s2) const;
	};

	typedef std::set<IniSectionA*, ci_less_a> SecIndexA;

#ifdef _WIN32
	// Added for VC6 Support
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
	friend class IniSectionA;
#endif
#endif

public:
	// Adds a section to the IniFileA object, returns a IniFileA pointer to the new or existing object
	IniSectionA* addSection(const std::string& section);

	// Removes section by pointer
	void removeSection(IniSectionA* section);

	// Removes a section by its name sSection
	void removeSection(const std::string& section);

	// Removes all existing sections
	void removeAllSections(void);

	// Returns a IniSectionA* to the section by name, NULL if it was not found
	IniSectionA* getSection(const std::string& section) const;

	// Returns all sections in the inifile by SecIndex, only to be used for enumeration (DO NOT KEEP THE REF OR TRY TO DELETE STUFF!)
	const SecIndexA& getSections(void) const;

	// Returns a KeyValue at a certain section
	std::string getKeyValue(const std::string& section, const std::string& key) const;

	// Sets a KeyValuePair at a certain section
	void setKeyValue(const std::string& section, const std::string& key, const std::string& value);

	// Renames an existing section returns true on success, false if the section didn't exist or there was another section with the same sNewSectionName
	bool renameSection(const std::string& sectionName, const std::string& newSectionName);

	// Renames an existing key returns true on success, false if the key didn't exist or there was another section with the same sNewSectionName
	bool renameKey(const std::string& sectionName, const std::string& keyName , const std::string& newKeyName);

private:
	SecIndexA::const_iterator _find_sec(const std::string& section) const;
	SecIndexA::iterator _find_sec(const std::string& section);

private:
	IniFileA(const IniFileA&);
	IniFileA& operator=(const IniFileA&);

	// List of IniSectionA pointers ( List of sections in the class )
	SecIndexA mSections;
};

// Basic typedefs for ease of use
typedef IniFileA::IniMergeA IniMergeA;
typedef IniFileA::IniSectionA IniSectionA;
typedef IniSectionA::IniKeyA IniKeyA;

// Pointers
typedef IniFileA* PINIA;
typedef IniKeyA* PINIKEYA;
typedef IniSectionA* PINISECA;

// Map Types
typedef IniSectionA::KeyIndexA KeyIndexA;
typedef IniFileA::SecIndexA SecIndexA;

} // end namespace cell


// Stream Helpers
std::ostream& operator<<(std::ostream& output, cell::IniFileA& obj);
std::istream& operator>>(std::istream& input, cell::IniFileA& obj);
std::istream& operator>>(std::istream& input, cell::IniMergeA merger);

namespace cell
{
// Unicode Class Definition

#define INI_TOKEN_A_UNICODE L"\a"	// I.E. Item1;Item2;Item3 - '\a' used in place of ';'
#define INI_TOKEN_B_UNICODE L"\b"	// I.E. Item1,Item1b;Item2,Item2b;Item3,Item3b - '\b' used in place of ','
#define INI_EMPTY_UNICODE L"*"		// Used to indicate empty value in token string. I.E. *;Item2;*;Item3;

/** Unicode 版 IniFile
*/
class _CellExport IniFileW : public GeneralAlloc
{
public:
	static const wchar_t* const LF;

public:
	/**
	*/
	IniFileW(void);

	/**
	*/
	virtual ~IniFileW(void);

	/** Used to save the data back to the file or your choice
	*/
	bool save(const std::wstring& fileName);

	/** Save data to an output stream
	*/
	void save(std::wostream& output);

	/** Loads the Reads the data in the ini file into the IniFile object
	*/
	bool load(const std::wstring& fileName , bool merge = false);

	/** Load data from an input stream
	*/
	void load(std::wistream& input , bool merge = false);

public:
	/**
	*/
	class _CellExport IniMergeW
	{
	public:
		explicit IniMergeW(IniFileW& ini);

		std::wistream &operator()(std::wistream& input) const;
	private:
		IniFileW& mIni;
	};

public:

#ifdef _WIN32
	// Added for versions earlier than VS2008
#if defined(_MSC_VER) && (_MSC_VER <= 1400)
	struct ci_less_w;
#endif
#endif

	class _CellExport IniSectionW : public GeneralAlloc
	{
		friend class IniFileW;	// Allow IniFileW to create sections

	#ifdef _WIN32
		// Added for versions earlier than VS2008
	#if defined(_MSC_VER) && (_MSC_VER <= 1400)
		friend struct ci_less_w;
	#endif
	#endif

	public:

	#ifdef _WIN32
		// Added for versions earlier than VS2008
	#if defined(_MSC_VER) && (_MSC_VER <= 1400)
		struct ci_less_w;
	#endif
	#endif

		class _CellExport IniKeyW : public GeneralAlloc
		{
			friend class IniSectionW;	// Allow IniSectionW to create keys

		#ifdef _WIN32
			// Added for versions earlier than VS2008
		#if defined(_MSC_VER) && (_MSC_VER <= 1400)
			friend struct ci_less_w;
		#endif
		#endif

		private:
			IniKeyW(IniSectionW* section , const std::wstring& keyName);
			IniKeyW(const IniKeyW&);
			IniKeyW& operator=(const IniKeyW&);
			virtual ~IniKeyW(void);

		public:
			// Sets the value of the key
			void setValue(const std::wstring& value);

			// Returns the value of the key
			const std::wstring& getValue(void) const;

			// Sets the key name, returns true on success, fails if the section
			// name sKeyName already exists
			bool setKeyName(const std::wstring& keyName);

			// Returns the name of the Key
			const std::wstring& getKeyName(void) const;

		private:
			// Pointer to the parent IniSectionW
			IniSectionW* mSection;
			// Name of the Key
			std::wstring mKeyName;
			// Value associated
			std::wstring mValue;
		};

		// Typedef of set of IniKeyW pointers
		struct ci_less_w
		{
			ci_less_w(void);
			~ci_less_w(void);

			bool operator() (const IniKeyW* s1, const IniKeyW* s2) const;
		};

		typedef std::set<IniKeyW*, ci_less_w> KeyIndexW;

	#ifdef _WIN32
		// Added for VC6 Support
	#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
		friend class IniKeyW;
	#endif
	#endif

	private:
		IniSectionW(IniFileW* iniFile , const std::wstring& sectionName);
		IniSectionW(const IniSectionW&);
		IniSectionW& operator=(const IniSectionW&);
		virtual ~IniSectionW(void);

	public:
		// Adds a key to the IniSectionW object, returns a IniKeyW pointer to the new or existing object
		IniKeyW* addKey(const std::wstring& keyName);

		// Removes a single key by pointer
		void removeKey(IniKeyW* key);

		// Removes a single key by string
		void removeKey(const std::wstring& key);

		// Removes all the keys in the section
		void removeAllKeys(void);

		// Returns a IniKeyW pointer to the key by name, NULL if it was not found
		IniKeyW* getKey(const std::wstring& keyName) const;

		// Returns all keys in the section by KeyIndex only to be used for enumeration
		const KeyIndexW& getKeys(void) const;

		// Returns a KeyValue at a certain section
		std::wstring getKeyValue(const std::wstring& key) const;

		// Sets a KeyValuePair at a certain section
		void setKeyValue(const std::wstring& key, const std::wstring& value);

		// Sets the section name, returns true on success, fails if the section
		// name sSectionName already exists
		bool setSectionName(const std::wstring& sectionName);

		// Returns the section name
		const std::wstring& getSectionName(void) const;

	private:
		KeyIndexW::const_iterator _find_key( const std::wstring& sKeyName ) const;
		KeyIndexW::iterator _find_key( const std::wstring& sKeyName );

	private:
		// IniFileW pointer back to the object that instanciated the section
		IniFileW* mIniFile;
		// Name of the section
		std::wstring mSectionName;
		// List of IniKeyW pointers ( Keys in the section )
		KeyIndexW mKeys;
	};

	// Typedef of a List of IniSectionW pointers
	struct ci_less_w
	{
		ci_less_w(void);
		~ci_less_w(void);

		bool operator() (const IniSectionW* s1, const IniSectionW* s2) const;
	};

	typedef std::set<IniSectionW*, ci_less_w> SecIndexW;

#ifdef _WIN32
	// Added for VC6 Support
#if defined(_MSC_VER) && (_MSC_VER >= 1200) && (_MSC_VER < 1300)
	friend class IniSectionW;
#endif
#endif

public:
	// Adds a section to the IniFileW object, returns a IniFileW pointer to the new or existing object
	IniSectionW* addSection(const std::wstring& section);

	// Removes section by pointer
	void removeSection(IniSectionW* section);

	// Removes a section by its name sSection
	void removeSection(const std::wstring& section);

	// Removes all existing sections
	void removeAllSections(void);

	// Returns a IniSectionW* to the section by name, NULL if it was not found
	IniSectionW* getSection(const std::wstring& section) const;

	// Returns all sections in the inifile by SecIndex, only to be used for enumeration (DO NOT KEEP THE REF OR TRY TO DELETE STUFF!)
	const SecIndexW& getSections(void) const;

	// Returns a KeyValue at a certain section
	std::wstring getKeyValue(const std::wstring& section, const std::wstring& key) const;

	// Sets a KeyValuePair at a certain section
	void setKeyValue(const std::wstring& section, const std::wstring& key, const std::wstring& value);

	// Renames an existing section returns true on success, false if the section didn't exist or there was another section with the same sNewSectionName
	bool renameSection(const std::wstring& sectionName, const std::wstring& newSectionName);

	// Renames an existing key returns true on success, false if the key didn't exist or there was another section with the same sNewSectionName
	bool renameKey(const std::wstring& sectionName, const std::wstring& keyName, const std::wstring& newKeyName);

private:
	SecIndexW::const_iterator _find_sec( const std::wstring& sSection ) const;
	SecIndexW::iterator _find_sec( const std::wstring& sSection );

private:
	IniFileW(const IniFileW&);
	IniFileW& operator=(const IniFileW&);
	// List of IniSectionW pointers ( List of sections in the class )
	SecIndexW mSections;
};

// Basic typedefs for ease of use
typedef IniFileW::IniMergeW IniMergeW;
typedef IniFileW::IniSectionW IniSectionW;
typedef IniSectionW::IniKeyW IniKeyW;

// Pointers
typedef IniFileW* PINIW;
typedef IniKeyW* PINIKEYW;
typedef IniSectionW* PINISECW;

// Map Types
typedef IniSectionW::KeyIndexW KeyIndexW;
typedef IniFileW::SecIndexW SecIndexW;

} // end namespace cell


std::wostream& operator<<(std::wostream& output, cell::IniFileW& obj);
std::wistream& operator>>(std::wistream& input, cell::IniFileW& obj);
std::wistream& operator>>(std::wistream& input, cell::IniMergeW merger);

namespace cell
{
// Additional defines
#ifdef _UNICODE
	#define INI_TOKEN_A INI_TOKEN_UNICODE
	#define INI_TOKEN_B INI_TOKEN_UNICODE
	#define INI_EMPTY INI_EMPTY_UNICODE
	typedef IniMergeW INIMerge;
	typedef IniFileW IniFile;
	typedef IniSectionW INISection;
	typedef IniKeyW INIKey;
	typedef PINIW PINI;
	typedef PINIKEYW PINIKEY;
	typedef PINISECW PINISEC;
	typedef KeyIndexW KeyIndex;
	typedef SecIndexW SecIndex;
#else
	#define INI_TOKEN_A INI_TOKEN_ANSI
	#define INI_TOKEN_B INI_TOKEN_ANSI
	#define INI_EMPTY INI_EMPTY_ANSI

	typedef IniMergeA INIMerge;
	typedef IniFileA IniFile;
	typedef IniSectionA INISection;
	typedef IniKeyA INIKey;
	typedef PINIA PINI;
	typedef PINIKEYA PINIKEY;
	typedef PINISECA PINISEC;
	typedef KeyIndexA KeyIndex;
	typedef SecIndexA SecIndex;
#endif // _UNICODE

/** @} */
/** @} */
} // end namespace cell

#endif // CellIniFile_h
